package com.panopset.swing;

import static com.panopset.Util.getCanonicalPath;
import static com.panopset.Util.x;

import java.io.File;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
import java.util.Map.Entry;

import com.panopset.Base64;
import com.panopset.Strings;
import com.panopset.gui.container.PersistentContainer;
import com.panopset.io.LockedWriter;

/**
 * Document to GUI Scaffolding.
 * 
 * @author Karl Dinwiddie.
 * 
 */
public final class Scaffolding {

	private final Vector<Document> dox = new Vector<Document>();

	private final Vector<PersistentContainer> pcs = new Vector<PersistentContainer>();

	/**
	 * @param applicationFrame
	 *            App frame.
	 */
	void doNew() {
		userChooseAppDirectory();
		flush();
	}

	/**
	 * @param applicationFrame
	 *            App frame.
	 */
	void doOpen() {
		flush();
		userChooseAppDirectory();
		load();
	}

	/**
	 * @param applicationFrame
	 *            App frame.
	 */
	void doSave() {
		flush();
	}

	/**
	 * @param applicationFrame
	 *            App frame.
	 */
	void doSaveAs() {
		flush();
		userChooseAppDirectory();
		flush();
	}

	/**
	 * Get a value from the current map document.
	 * 
	 * @param key
	 *            Key.
	 * @return Value.
	 */
	public String get(final String key) {
		return map.get(key);
	}

	private void userChooseAppDirectory() {
		File newFile = com.panopset.UtilIO.getDirectoryFromUser(getDir(),
				x("Please select application directory"));
		setDir(newFile);
	}

	/**
	 * Put a value to the current map document.
	 * 
	 * @param key
	 *            Key.
	 * @param value
	 *            Value.
	 */
	public void put(final String key, final String value) {
		map.put(key, value);
	}

//	private final Map<String, String> map = Collections
//			.synchronizedSortedMap(new TreeMap<String, String>());

	private final Map<String, String> map = new HashMap<String, String>();

	private File dir;

	public void setDir(final File directory) {
		if (isSafeAppDirectory(directory)) {
			dir = directory;
		}
	}

	/**
	 * @return Application directory within the base directory.
	 */
	public File getDir() {
		if (dir == null) {
			String appName = App.app.getApplicationName();
			String hd = App.app.getHome();
			File probe = new File(combinePaths(hd, appName));
			if (isSafeAppDirectory(probe)) {
				return probe;
			} else {
				probe = new File(combinePaths(hd, "." + appName));
			}
			while (!isSafeAppDirectory(probe)) {
				probe = com.panopset.UtilIO.getDirectoryFromUser(probe,
						"Please select application base directory");
				if (probe.exists() && probe.isDirectory()) {
					// user selected, okay to flag
					flagDirectory(probe);
					return dir;
				}
			}
		}
		return null;
	}

	private String getSimpleFlagName() {
		StringWriter sw = new StringWriter();
		sw.append(App.app.getApplicationName());
		sw.append("_READ_ME.txt");
		return sw.toString();
	}

	private File getFlag(File dir) {
		return new File(combinePaths(com.panopset.Util.getCanonicalPath(dir),
				getSimpleFlagName()));
	}

	private void flagDirectory(File dir) {
		File flag = getFlag(dir);
		if (!flag.exists()) {
			LockedWriter lw = new LockedWriter(flag);
			lw.write("This file resides in an application directory for ");
			lw.write(App.app.getApplicationName());
			lw.writeln(".");
			lw.writeln("");
			lw.writeln("Last updated:");
			lw.writeln(com.panopset.Util.getCurrentDateTime());
			lw.close();
		}
	}

	private final boolean isSafeAppDirectory(final File dir) {
		if (dir.exists() && dir.isFile()) {
			return false;
		}
		if (dir.exists()) {
			if (dir.isFile()) {
				return false;
			} else {
				if (getFlag(dir).exists()) {
					return true;
				} else {
					return false;
				}
			}
		} else {
			dir.mkdirs();
			flagDirectory(dir);
			return true;
		}
	}

	public static final String combinePaths(final String path0,
			final String path1) {
		StringWriter sw = new StringWriter();
		sw.append(path0);
		sw.append(Strings.FILE_SEP);
		sw.append(path1);
		return sw.toString();
	}

	/**
	 * Register a document stored in the Scaffolding base directory.
	 * 
	 * @param document
	 *            Document.
	 */
	void registerDocument(final Document document) {
		if (!dox.contains(document)) {
			dox.add(document);
		}
	}

	private File createProps() {
		return new File(combinePaths(getCanonicalPath(getDir()), "app.txt"));
	}

	/**
	 * Load all data.
	 */
	void load() {
		for (Entry<String, String> e : com.panopset.UtilIO.loadMap(
				createProps()).entrySet()) {
			map.put(e.getKey(), e.getValue());
		}
	}

	void loadGui() {
		for (Document doc : dox) {
			doc.load(getDir());
		}
		for (PersistentContainer pc : pcs) {
			pc.loadMap(map);
		}
	}

	/**
	 * Save all data.
	 */
	void flush() {
		for (PersistentContainer pc : pcs) {
			map.put(pc.getKey(), Base64.encode(pc.getValue()));
		}
		com.panopset.UtilIO.saveMap(map, createProps());
		for (Document doc : dox) {
			doc.save(getDir());
		}
	}

	public void addPc(PersistentContainer persistentContainer) {
		pcs.add(persistentContainer);
	}
}
